/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.    
*/
using System;
using System.Collections;
using Velocity4Net.Runtime;
using Velocity4Net.Ctx;
using System.Collections.Generic;
using Velocity4Net.Util;

namespace Velocity4Net.App.Events
{
    /**
     * Stores the event handlers. Event handlers can be assigned on a per
     * VelocityEngine instance basis by specifying the class names in the
     * velocity.properties file. Event handlers may also be assigned on a per-page
     * basis by creating a new instance of EventCartridge, adding the event
     * handlers, and then calling attachToContext. For clarity, it's recommended
     * that one approach or the other be followed, as the second method is primarily
     * presented for backwards compatibility.
     *
     * <P>
     * Note that Event Handlers follow a filter pattern, with multiple event
     * handlers allowed for each event. When the appropriate event occurs, all the
     * appropriate event handlers are called in the sequence they were added to the
     * Event Cartridge. See the javadocs of the specific event handler interfaces
     * for more details.
     *
     * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a>
     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr. </a>
     * @author <a href="mailto:j_a_fernandez@yahoo.com">Jose Alberto Fernandez </a>
     * @version $Id: EventCartridge.java 685685 2008-08-13 21:43:27Z nbubna $
     */
    public class EventCartridge
    {
        private IList referenceHandlers = new ArrayList();
        private IList nullSetHandlers = new ArrayList();
        private IList methodExceptionHandlers = new ArrayList();
        private IList includeHandlers = new ArrayList();
        private IList invalidReferenceHandlers = new ArrayList();

        /**
         * Ensure that handlers are not initialized more than once.
         */
        HashSet<object> initializedHandlers = new HashSet<object>();

        /**
         *  Adds an event handler(s) to the Cartridge.  This method
         *  will find all possible event handler interfaces supported
         *  by the passed in object.
         *
         *  @param ev object impementing a valid EventHandler-derived interface
         *  @return true if a supported interface, false otherwise or if null
         */
        public bool addEventHandler(EventHandler ev)
        {
            if (ev == null)
            {
                return false;
            }

            bool found = false;

            if (ev is ReferenceInsertionEventHandler)
            {
                addReferenceInsertionEventHandler((ReferenceInsertionEventHandler)ev);
                found = true;
            }

            if (ev is NullSetEventHandler)
            {
                addNullSetEventHandler((NullSetEventHandler)ev);
                found = true;
            }

            if (ev is MethodExceptionEventHandler)
            {
                addMethodExceptionHandler((MethodExceptionEventHandler)ev);
                found = true;
            }

            if (ev is IncludeEventHandler)
            {
                addIncludeEventHandler((IncludeEventHandler)ev);
                found = true;
            }

            if (ev is InvalidReferenceEventHandler)
            {
                addInvalidReferenceEventHandler((InvalidReferenceEventHandler)ev);
                found = true;
            }

            return found;
        }

        /**
          *  Add a reference insertion event handler to the Cartridge.
          *
          *  @param ev ReferenceInsertionEventHandler
         * @since 1.5
         */
        public void addReferenceInsertionEventHandler(ReferenceInsertionEventHandler ev)
        {
            referenceHandlers.Add(ev);
        }

        /**
          *  Add a null set event handler to the Cartridge.
          *
          *  @param ev NullSetEventHandler
          * @since 1.5
          */
        public void addNullSetEventHandler(NullSetEventHandler ev)
        {
            nullSetHandlers.Add(ev);
        }

        /**
         *  Add a method exception event handler to the Cartridge.
         *
         *  @param ev MethodExceptionEventHandler
         *  @since 1.5
         */
        public void addMethodExceptionHandler(MethodExceptionEventHandler ev)
        {
            methodExceptionHandlers.Add(ev);
        }

        /**
         *  Add an include event handler to the Cartridge.
         *
         *  @param ev IncludeEventHandler
         *  @since 1.5
         */
        public void addIncludeEventHandler(IncludeEventHandler ev)
        {
            includeHandlers.Add(ev);
        }

        /**
         *  Add an invalid reference event handler to the Cartridge.
         *
         *  @param ev InvalidReferenceEventHandler
         *  @since 1.5
         */
        public void addInvalidReferenceEventHandler(InvalidReferenceEventHandler ev)
        {
            invalidReferenceHandlers.Add(ev);
        }


        /**
         * Removes an event handler(s) from the Cartridge. This method will find all
         * possible event handler interfaces supported by the passed in object and
         * remove them.
         *
         * @param ev  object impementing a valid EventHandler-derived interface
         * @return true if event handler was previously registered, false if not
         *         found
         */
        public bool removeEventHandler(EventHandler ev)
        {
            if (ev == null)
            {
                return false;
            }

            if (ev is ReferenceInsertionEventHandler)
            {
                referenceHandlers.Remove(ev);
            }
            if (ev is NullSetEventHandler)
                nullSetHandlers.Remove(ev);

            if (ev is MethodExceptionEventHandler)
                methodExceptionHandlers.Remove(ev);

            if (ev is IncludeEventHandler)
                includeHandlers.Remove(ev);

            if (ev is InvalidReferenceEventHandler)
                invalidReferenceHandlers.Remove(ev);

            return true;
        }

        /**
         * Iterate through all the stored ReferenceInsertionEventHandler objects
         * 
         * @return iterator of handler objects, null if there are not handlers
         * @since 1.5
         */
        public IEnumerable ReferenceInsertionEventHandlers
        {
            get
            {
                return referenceHandlers.Count == 0 ? null : referenceHandlers;
            }
        }

        /**
         * Iterate through all the stored NullSetEventHandler objects
         * 
         * @return iterator of handler objects
         * @since 1.5
         */
        public IEnumerable NullSetEventHandlers
        {
            get
            {
                return nullSetHandlers;
            }
        }

        /**
         * Iterate through all the stored MethodExceptionEventHandler objects
         * 
         * @return iterator of handler objects
         * @since 1.5
         */
        public IEnumerable MethodExceptionEventHandlers
        {
            get
            {
                return methodExceptionHandlers;
            }
        }

        /**
         * Iterate through all the stored IncludeEventHandlers objects
         * 
         * @return iterator of handler objects
         */
        public IEnumerable IncludeEventHandlers
        {
            get
            {
                return includeHandlers;
            }
        }

        /**
         * Iterate through all the stored InvalidReferenceEventHandlers objects
         * 
         * @return iterator of handler objects
         * @since 1.5
         */
        public IEnumerable InvalidReferenceEventHandlers
        {
            get
            {
                return invalidReferenceHandlers;
            }
        }

        /**
         *  Attached the EventCartridge to the context
         *
         *  Final because not something one should mess with lightly :)
         *
         *  @param context context to attach to
         *  @return true if successful, false otherwise
         */
        public bool AttachToContext(IContext context)
        {
            if (context is InternalEventContext)
            {
                InternalEventContext iec = (InternalEventContext)context;

                iec.AttachEventCartridge(this);

                /**
                 * while it's tempting to call setContext on each handler from here,
                 * this needs to be done before each method call.  This is
                 * because the specific context will change as inner contexts
                 * are linked in through macros, foreach, or directly by the user.
                 */

                return true;
            }
            else
            {
                return false;
            }
        }

        /**
         * Initialize the handlers.  For global handlers this is called when Velocity
         * is initialized. For local handlers this is called when the first handler
         * is executed.  Handlers will not be initialized more than once.
         * 
         * @param rs
         * @throws Exception
         * @since 1.5
         */
        public void Initialize(RuntimeServices rs)
        {

            foreach (EventHandler eh in referenceHandlers)
            {
                if ((eh is RuntimeServicesAware) &&
                        !initializedHandlers.Contains(eh))
                {
                    ((RuntimeServicesAware)eh).RuntimeServices = rs;
                    initializedHandlers.Add(eh);
                }
            }

            foreach (EventHandler eh in nullSetHandlers)
            {
                if ((eh is RuntimeServicesAware) &&
                        !initializedHandlers.Contains(eh))
                {
                    ((RuntimeServicesAware)eh).RuntimeServices = rs;
                    initializedHandlers.Add(eh);
                }
            }

            foreach (EventHandler eh in methodExceptionHandlers)
            {
                if ((eh is RuntimeServicesAware) &&
                        !initializedHandlers.Contains(eh))
                {
                    ((RuntimeServicesAware)eh).RuntimeServices = rs;
                    initializedHandlers.Add(eh);
                }
            }

            foreach (EventHandler eh in includeHandlers)
            {
                if ((eh is RuntimeServicesAware) &&
                        !initializedHandlers.Contains(eh))
                {
                    ((RuntimeServicesAware)eh).RuntimeServices = rs;
                    initializedHandlers.Add(eh);
                }
            }

            foreach (EventHandler eh in invalidReferenceHandlers)
            {
                if ((eh is RuntimeServicesAware) &&
                        !initializedHandlers.Contains(eh))
                {
                    ((RuntimeServicesAware)eh).RuntimeServices = rs;
                    initializedHandlers.Add(eh);
                }
            }

        }


    }
}
